﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Pixysoft.Threads
{
    public class MapReduceInvoker
    {
        List<AutoResetEvent> autoEvents = new List<AutoResetEvent>();


        public delegate void VoidInvoker();

        public delegate void ParamInvoker(params object[] paras);


        private static volatile MapReduceInvoker instance;

        private static object syncRoot = new Object();

        private static MapReduceInvoker Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        if (instance == null)
                        {
                            instance = new MapReduceInvoker();
                        }
                    }
                }
                return instance;
            }
        }


        public static MapReduceInvoker Create()
        {
            return new MapReduceInvoker();
        }

        public static void Invoke(VoidInvoker invoker)
        {
            Instance.Invokes(invoker);
        }

        public static void Invoke(ParamInvoker invoker, params object[] paras)
        {
            Instance.Invokes(invoker, paras);
        }

        public static bool Wait()
        {
            bool result = Instance.Waits();

            return result;
        }

        public static bool Wait(int millisecondTimeout)
        {
            bool result = Instance.Waits(millisecondTimeout);

            return result;
        }


        public void Invokes(VoidInvoker invoker)
        {
            AutoResetEvent aevent = new AutoResetEvent(false);
            autoEvents.Add(aevent);
            new VoidInvokerThread(aevent).Invoke(invoker);
        }

        public void Invokes(ParamInvoker invoker, params object[] paras)
        {
            AutoResetEvent aevent = new AutoResetEvent(false);
            autoEvents.Add(aevent);
            new ParamInvokerThread(aevent).Invoke(invoker, paras);
        }

        public bool Waits()
        {
            foreach (AutoResetEvent aevent in autoEvents)
            {
                if (!aevent.WaitOne())
                    return false;
            }

            autoEvents.Clear();

            return true;
        }

        public bool Waits(int millisecondTimeOut)
        {
            foreach (AutoResetEvent aevent in autoEvents)
            {
                if (!aevent.WaitOne(millisecondTimeOut, false))
                    return false;
            }

            autoEvents.Clear();

            return true;
        }


        class VoidInvokerThread
        {
            VoidInvoker method;
            AutoResetEvent autoEvent;

            internal VoidInvokerThread(AutoResetEvent autoEvent)
            {
                this.autoEvent = autoEvent;
            }

            public void Invoke(VoidInvoker method)
            {
                this.method = method;
                ThreadPool.QueueUserWorkItem(new WaitCallback(CallThread), null);
            }

            private void CallThread(object state)
            {
                if (method != null)
                    method();

                autoEvent.Set();

                this.autoEvent = null;
                this.method = null;
            }
        }

        class ParamInvokerThread
        {
            ParamInvoker method = null;
            object[] paras = null;
            AutoResetEvent autoEvent = null;

            internal ParamInvokerThread(AutoResetEvent autoEvent)
            {
                this.autoEvent = autoEvent;
            }

            public void Invoke(ParamInvoker method, params object[] paras)
            {
                this.method = method;
                this.paras = paras;
                ThreadPool.QueueUserWorkItem(new WaitCallback(CallThread), null);
            }

            private void CallThread(object state)
            {
                if (method != null)
                    method(paras);

                autoEvent.Set();

                this.autoEvent = null;
                this.method = null;
                this.paras = null;
            }
        }
    }


    public class MapReduceInvoker<T>
    {
        List<AutoResetEvent> autoEvents = new List<AutoResetEvent>();

        public delegate void GenericInvoker(T value);


        private static volatile MapReduceInvoker<T> instance;

        private static object syncRoot = new Object();

        private static MapReduceInvoker<T> Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        if (instance == null)
                        {
                            instance = new MapReduceInvoker<T>();
                        }
                    }
                }
                return instance;
            }
        }


        public static MapReduceInvoker<T> Create()
        {
            return new MapReduceInvoker<T>();
        }

        public static void Invoke(GenericInvoker invoker, T value)
        {
            Instance.Invokes(invoker, value);
        }

        public static bool Wait()
        {
            bool result = Instance.Waits();

            return result;
        }

        public static bool Wait(int millisecondTimeout)
        {
            bool result = Instance.Waits(millisecondTimeout);

            return result;
        }




        public void Invokes(GenericInvoker invoker, T value)
        {
            AutoResetEvent aevent = new AutoResetEvent(false);
            autoEvents.Add(aevent);
            new GenericInvokerThread(aevent).Invoke(invoker, value);
        }

        public bool Waits()
        {
            foreach (AutoResetEvent aevent in autoEvents)
            {
                if (!aevent.WaitOne())
                    return false;
            }

            autoEvents.Clear();

            return true;
        }

        public bool Waits(int millisecondTimeOut)
        {
            foreach (AutoResetEvent aevent in autoEvents)
            {
                if (!aevent.WaitOne(millisecondTimeOut, false))
                    return false;
            }

            autoEvents.Clear();

            return true;
        }

        class GenericInvokerThread
        {
            GenericInvoker method = null;
            T value = default(T);
            AutoResetEvent autoEvent = null;

            internal GenericInvokerThread(AutoResetEvent autoEvent)
            {
                this.autoEvent = autoEvent;
            }

            public void Invoke(GenericInvoker method, T value)
            {
                this.method = method;
                this.value = value;
                ThreadPool.QueueUserWorkItem(new WaitCallback(CallThread), null);
            }

            private void CallThread(object state)
            {
                if (method != null)
                    method(value);

                autoEvent.Set();

                this.autoEvent = null;
                this.method = null;
                this.value = default(T);
            }
        }
    }
}
